package thread_case;

import java.util.PriorityQueue;

// 通过 MyTimerTask 类,来描述一个任务
class MyTimerTask implements Comparable<MyTimerTask> {
    // 实际任务要执行的代码
    private Runnable runnable;
    // 在什么时间来执行这个任务
    // 此处的 time 是一个 ms 级别的时间戳
    private long time;
    // delay 是一个相对时间
    public MyTimerTask(Runnable runnable, long delay) {
        this.runnable = runnable;
        // 计算真正要执行任务的绝对时间
        // 使用绝对时间方便判定当前任务是否到达时间
        time = System.currentTimeMillis() + delay;
    }
    // 获取 time 的值
    public long getTime() {
        return time;
    }
    // 执行任务
    public void run() {
        runnable.run();
    }
    // 用来进行比较
    // 此处期望根据时间,时间小的优先级更高
    @Override
    public int compareTo(MyTimerTask o) {
        return (int) (this.time - o.time);
    }
}

// 通过 MyTimer 来表示一个定时器
class MyTimer {
    // 任务队列,是定时器的核心结构
    private PriorityQueue<MyTimerTask> tasks = new PriorityQueue<>();
    // 创建一个锁对象
    private Object locker = new Object();
    public void schedule(Runnable runnable,long delay) {
        synchronized (locker) {
            // 根据参数构造任务
            MyTimerTask task = new MyTimerTask(runnable,delay);
            // 向任务队列中添加任务
            tasks.add(task);
            // 添加完任务后,就可以唤醒扫描线程的 wait 方法,来让扫描线程扫描任务
            locker.notify();
        }
    }
    // 负责扫描任务队列,执行到达执行时间的任务
    private Thread t = null;
    // 构造方法,创建扫描线程,让扫描线程来完成判定和执行
    public MyTimer() {
        t = new Thread(()->{
            // 扫描线程需要反复循环来扫描队首的任务,然后判定当前队首任务是否到达执行时间
            // 如果执行时间到了就执行这个任务,并把这个任务从队列中删除掉
            // 如果时间没有到,就什么都不做
            // 使用 Thread.currentThread().isInterrupted() 来判定当前线程是否被中断
            // 可以方便结束定时器
            while (!Thread.currentThread().isInterrupted()) {
                try {
                    synchronized (locker) {
                        // 如果当前任务队列为空,就不要再循环扫描了
                        // 而是调用 wait 进行等待
                        // 等有任务添加到任务队列后唤醒此处再进行循环扫描
                        if (tasks.isEmpty()) {
                            locker.wait();
                        }
                        // 获取当前任务队列的队首任务
                        MyTimerTask task = tasks.peek();
                        // 判定当前任务是否到达执行时间
                        if (task.getTime() <= System.currentTimeMillis()) {
                            // 当前时间已经到达了任务的执行时间
                            // 执行任务
                            task.run();
                            // 将任务从队列中删除
                            tasks.poll();
                        }else {
                            // 当前时间还没有到.先不执行
                            // 调用带有时间的 wait
                            // 等任务时间到了自动唤醒
                            // 或者有新的任务添加唤醒后来再次扫描
                            locker.wait(task.getTime() - System.currentTimeMillis());
                        }
                    }
                }catch (InterruptedException e){
                    break;
                }
            }
        });
        // 启动线程
        t.start();
    }
    // cancel 方法用来结束定时器
    public void cancel() {
        // 中断扫描线程 t
        t.interrupt();
    }
}

public class ThreadDemo10 {
    public static void main(String[] args) throws InterruptedException {
        // 创建 MyTimer 对象,构建一个定时器
        MyTimer timer = new MyTimer();
        // 使用 schedule 方法来添加任务并指定多久后执行(单位: ms)
        timer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello 3000");
            }
        },3000);
        timer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello 2000");
            }
        },2000);
        timer.schedule(new Runnable() {
            @Override
            public void run() {
                System.out.println("hello 1000");
            }
        },1000);
        // 5秒后关闭定时器
        Thread.sleep(5000);
        timer.cancel();
    }
}
